package com.xohm.daemon;

import java.io.*;
import java.net.*;
import java.util.*;
import com.xohm.daemon.commands.*;

public class Worker implements HttpConstants, Runnable
{
	private WebServer webServer = null;
	private Control cmiControl  = new Control();
	private Command cmiCommand  = new Command();
	
	public final static int BUF_SIZE = 2048;
	public final static byte[] EOL = { (byte) '\r', (byte) '\n' };
	public final static String EOLEOL = "\r\n\r\n";
	public final static String EMPTY_STRING = "";
	public final static String SPACE        = " ";

	private static final String endTag1 = "</WiMaxDaemon>";
	private static final String endTag2 = "/>";
	
	private static final String COMMAND         = "command";
	private static final String CONTROL_NUMBER  = "controlNumber";
	
	private static final String REQUEST_CONTROL = "requestControl";
	private static final String KILL_PROCESS    = "kill";

	public byte[] buf;     // buffer to use for requests
	private Socket socket; // Socket to client we're handling
	public static java.util.Hashtable<String, String> map = new java.util.Hashtable<String, String>();  // Mapping of file extensions to content-types
	static
	{
		fillMap();
	}

	public Worker(WebServer webServer)
	{
		this.webServer = webServer;
		buf = new byte[BUF_SIZE];
		socket = null;
	}

	public synchronized void setSocket(Socket socket)
	{
		this.socket = socket;
		notify();
	}

	public synchronized void run()
	{
		while (true)
		{
			if (socket == null)
			{
				/* nothing to do */
				try
				{
					wait();
				}
				catch (InterruptedException e)
				{
					continue;
				}
			}
			
			try
			{
				handleClient();
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}
	}

	private void handleClient() throws IOException
	{
		InputStream is = new BufferedInputStream(socket.getInputStream());
		PrintStream ps = new PrintStream(socket.getOutputStream());
		
		socket.setSoTimeout(webServer.timeout);

		/* zero out the buffer from last time */
		for (int i = 0; i<buf.length;i++)
		buf[i] = 0;

		try
		{
			// Only support HTTP POST/HEAD.
			InputStreamReader in = new InputStreamReader(is);
			BufferedReader buf = new BufferedReader(in);
			
			int contentLength = -1;
			String line = null;
			StringBuffer header  = new StringBuffer(1000);
			StringBuffer content = new StringBuffer(2000);
			boolean endOfContent = false;
			while (!endOfContent)
			{
				line = buf.readLine();
//System.out.println(line);
				if (line.startsWith("Content-Length"))
				{
//System.out.println("########### Content-Length='"+line+"'");
					contentLength = Integer.valueOf(line.substring(line.indexOf(SPACE)+1).trim()).intValue();
				}
				else
				{
					if (line.equals(EMPTY_STRING))
					{
						// Content
						for (int i=0;i<contentLength;i++)
						{
							content.append((char)buf.read());
						}
						endOfContent = true;
					}
					else
					{
						header.append(line);
					}
				}
			}

//System.out.println("headers =\n" + header);
//System.out.println("\ncontent =\n" + content);
			if (header != null && content != null)
			{
				//check that the method used was a POST
				if (header.indexOf("POST ") < 0)
				{
					// Only support POST's
					System.out.println("Only POST's are supported: " + socket.getInetAddress().getHostAddress() + " : " + header.toString());
					ps.print("HTTP/1.0 " + HTTP_BAD_METHOD + " unsupported method type: ");
					ps.print(header.substring(0, 5));
					ps.write(EOL);
					ps.flush();
					socket.close();
					return;
				}
	
				//check for valid URI of 'wimax'
				int start = header.indexOf(" ")+2;
				String wimaxName = (header.substring(start, start+5));
				if (wimaxName.equals("wimax"))
				{
					boolean OK = printHeaders(true, ps);
					if (OK) {       
						processRequest(header.toString(), content.toString(), ps);
					} else {
						send404(ps);
					}
				}
			}
			else
				send404(ps);
		}
		finally
		{
			socket.close();
			socket = null;
			webServer.threads.addElement(this);
		}
	}

	private boolean printHeaders(boolean correctUrl, PrintStream ps) throws IOException
	{
		boolean ok = false;
		if (correctUrl)
		{
			ps.print("HTTP/1.0 " + HTTP_OK + " OK");
			ps.write(EOL);
			ok = true;
		}
		else
		{
			ps.print("HTTP/1.0 " + HTTP_NOT_FOUND + " not found");
			ps.write(EOL);
			ok = false;
		}
		
		//System.out.println("From " + socket.getInetAddress().getHostAddress());
		ps.print("Server: WiMax Daemon");
		ps.write(EOL);
		ps.print("Date: " + (new Date()));
		ps.write(EOL);
		if (ok)
		{
			ps.print("Content-type: text/html");
			ps.write(EOL);
		}
		return ok;
	}

	private void send404(PrintStream ps) throws IOException
	{
		ps.write(EOL);
		ps.write(EOL);
		ps.println("Not Found - The requested resource was not found.\n");
	}

	private void processRequest(String header, String content, PrintStream ps) throws IOException
	{
		// Get the XML String from the content
		int endOfXmlString = -1;
		endOfXmlString = content.indexOf(endTag1)+endTag1.length();
		if (content.lastIndexOf(endTag2) > endOfXmlString)
			endOfXmlString = content.lastIndexOf(endTag2)+endTag2.length();
		
		String xml = content.substring(0, endOfXmlString);
		
		/** Parse the XML String:
		 *    Available Commands on localhost:
		 *      requestControl        - Only one application can have control and a control number is
		 *                              sent back to the app if control is approved.
		 *      connect               - Requires a control number
		 *      connectionStatus      - Anyone can ping
		 *      connectionStatistics  - Anyone can ping
		 *      linkStatus            - Anyone can ping
		 *      disconnect            - Requires a control number
		 *      kill                  - Requires a control number
		 */
		String command = null;
		if (xml.indexOf(COMMAND) > -1)
		{
			int start = xml.indexOf("\"", xml.indexOf(COMMAND))+1;
			command = xml.substring(start, xml.indexOf("\"", start));
		}
		
		String controlNumber = null;
		if (xml.indexOf(CONTROL_NUMBER) > -1)
		{
			int start = xml.indexOf(">", xml.indexOf(CONTROL_NUMBER))+1;
			controlNumber = xml.substring(start, xml.indexOf("<", start)).trim();
		}
		
		// Process the request
		String responseXml = processRequest(command, controlNumber, xml);
//System.out.println("Response XML:\n "+responseXml);
		
		// Reply back to the receiver
		ps.write(EOL);
		ps.write(responseXml.getBytes());
		ps.flush();
	}
	
	private String processRequest(String command, String controlNumber, String xmlRequest)
	{
		String responseXml = null;
		if (command.equals(REQUEST_CONTROL) || command.equals(KILL_PROCESS))
			responseXml = cmiControl.executeCommand(command, controlNumber, xmlRequest);
		else
			responseXml = cmiCommand.executeCommand(command, controlNumber, xmlRequest);

		return responseXml;
	}
	
	public static void setSuffix(String key, String value)
	{
		map.put(key, value);
	}

	public static void fillMap()
	{
		setSuffix("", "content/unknown");
		setSuffix(".uu", "application/octet-stream");
		setSuffix(".exe", "application/octet-stream");
		setSuffix(".ps", "application/postscript");
		setSuffix(".zip", "application/zip");
		setSuffix(".sh", "application/x-shar");
		setSuffix(".tar", "application/x-tar");
		setSuffix(".snd", "audio/basic");
		setSuffix(".au", "audio/basic");
		setSuffix(".wav", "audio/x-wav");
		setSuffix(".gif", "image/gif");
		setSuffix(".jpg", "image/jpeg");
		setSuffix(".jpeg", "image/jpeg");
		setSuffix(".htm", "text/html");
		setSuffix(".html", "text/html");
		setSuffix(".text", "text/plain");
		setSuffix(".c", "text/plain");
		setSuffix(".cc", "text/plain");
		setSuffix(".c++", "text/plain");
		setSuffix(".h", "text/plain");
		setSuffix(".pl", "text/plain");
		setSuffix(".txt", "text/plain");
		setSuffix(".java", "text/plain");
	}
}
